﻿using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.ViewModels;
using MugenMvvmToolkitTest.TestInfrastructure;
using MugenMvvmToolkitTest.TestModels;
using Should;

namespace MugenMvvmToolkitTest.ViewModel
{
    public abstract class CloseableViewModelTest : TestBase
    {
        #region Test methods

        [TestMethod]
        public void CloseCommandShouldCloseVm()
        {
            var param = new object();
            bool isInvoked = false;
            bool isClosingInvoked = false;
            var closeableViewModel = GetCloseableViewModel();
            closeableViewModel.Closing += (model, args, parameter) =>
                                                 {
                                                     parameter.ShouldEqual(param);
                                                     isClosingInvoked = true;
                                                 };
            closeableViewModel.Closed += (model, parameter) =>
                                                {
                                                    parameter.ShouldEqual(param);
                                                    isInvoked = true;
                                                };
            closeableViewModel.CloseCommand.Execute(param);
            isInvoked.ShouldBeTrue();
            isClosingInvoked.ShouldBeTrue();
        }

        [TestMethod]
        public void CloseCommandShouldCloseVmImmediateWithParameter()
        {
            bool isInvoked = false;
            bool isClosingInvoked = false;
            var closeableViewModel = GetCloseableViewModel();
            closeableViewModel.Closing += (model, args, parameter) =>
            {
                isClosingInvoked = true;
            };
            closeableViewModel.Closed += (model, parameter) =>
            {
                isInvoked = true;
            };
            closeableViewModel.CloseCommand.Execute(CloseableViewModelFacade.ImmediateCloseParameter);
            isClosingInvoked.ShouldBeFalse();
            isInvoked.ShouldBeTrue();
        }

        [TestMethod]
        public void CloseShouldThrowExceptionIfCallbackIsNull()
        {
            var closeableViewModel = GetCloseableViewModel();
            closeableViewModel.Closed += (model, parameter) =>
            {
                throw new TestException();
            };
            ShouldThrow<TestException>(() =>
            {
                var result = closeableViewModel.Close(null).Result;
            });
        }

        [TestMethod]
        public void CloseShouldRedirectExceptionToTask()
        {
            var exc = new Exception();

            var closeableViewModel = GetCloseableViewModel();
            closeableViewModel.Closed += (model, parameter) =>
            {
                throw exc;
            };
            var close = closeableViewModel.Close(null);
#if TASKSUPPORT
            close.Status.ShouldEqual(TaskStatus.Faulted);
            Exception exception = close.Exception;
            while (exception is AggregateException)
            {
                exception = exception.InnerException;
            }
            exception.ShouldEqual(exc);
#else
            close.Status.ShouldEqual(OperationStatus.Faulted);
            close.Exception.ShouldEqual(exc);
#endif
        }


        [TestMethod]
        public void CloseShouldReturnTrueIfCloseSuccess()
        {
            var closeableViewModel = GetCloseableViewModel();
            closeableViewModel.Close(null).Result.ShouldBeTrue();
        }

        [TestMethod]
        public void CloseShouldReturnFalseIfCloseNonSuccess()
        {
            var closeableViewModel = GetCloseableViewModel();
            closeableViewModel.Closing += (model, args, parameter) => args.Cancel = true;
            closeableViewModel.Close(null).Result.ShouldBeFalse();
        }

        #endregion

        #region Methods

        private INotifyViewModelClosing GetCloseableViewModel()
        {
            INotifyViewModelClosing viewModel = GetCloseableViewModelInternal();
            InitializeViewModel(viewModel, IocAdapter, GlobalSettings.DefaultActivationPolicy);
            return viewModel;
        }

        protected abstract INotifyViewModelClosing GetCloseableViewModelInternal();

        #endregion
    }
}